home *** CD-ROM | disk | FTP | other *** search
- ;
- ; *** Listing 2 ***
- ;
- ;These routines produce the effect of hardware sprites in software
- ; on IBM PC compatible computers. They put objects onto the screen
- ; in a manner which preserves the background, and produces no
- ; undesirable fringe or overlap effects. Operations which affect
- ; video buffer memory are performed as much as possible during
- ; video non-display periods to avoid other undesirable effects.
- ;
- ; Entry points and parameters:
- ;
- ; Initialize - Sets the background buffer address to be used to erase
- ; objects, resets internal flags and queue, and on EGAs
- ; sets up the use of the vertical interrupt to drive the
- ; drawing routines.
- ;
- ; Inputs - AX holds paragraph address of background buffer.
- ; Outputs - None.
- ;
- ; Terminate - Resets the EGA vertical interrupt hardware and vector
- ;
- ; Inputs - None.
- ; Outputs - None.
- ;
- ; Object_services - Sets X,Y and Form address for a given object
- ; to be drawn, and activates or deactivates the object.
- ;
- ; Inputs - CX holds X position in bytes (0-79) of upper
- ; left hand corner of object. 0 is leftmost.
- ; - BX holds Y position in lines (0-198) 0 is top.
- ; BX must be even! Objects cannot start on odd lines.
- ; Objects must also be an even number of lines high.
- ; - DI holds object number. Higher numbered objects
- ; will appear to be in front of lower numbered
- ; objects when they overlap.
- ; - SI holds the offset in the code segment of the form
- ; to be drawn for the object. A value of 0ffffh means
- ; that the object is to be erased, then ignored.
- ; Forms must be in the following format:
- ;
- ; byte 0 - height in lines (h)
- ; byte 1 - width in bytes (w)
- ; followed by w X h (mask word, image word) pairs.
- ;
- ; Outputs - None.
- ;
- ; Registers - All are saved, except flags
- ;
- ; Warning - No bounds checking is done. X,Y or object numbers
- ; out of range can send your program into hyperspace.
- ;
- ; Put_objects_on_screen - This routine should be called by a program
- ; running on a CGA to put the objects on the screen. It must be
- ; far-called as if it were an interrupt routine. For example:
- ;
- ; pushf
- ; call far ptr put_objects_on_screen
- ;
- ; For best results this routine should be called immediately
- ; upon the sensing of vertical retrace.
- ;
- ; Inputs - None.
- ; Outputs - None.
- ;
- ; Vert_int_modulo_count - This memory word is incremented each time
- ; the objects are put into the screen map. On EGAs it can be used
- ; to synchronize a program to the constant time base provided by
- ; the vertical interrupt.
- ;
- ;The flag below must be set properly before assembling this program
- ;
- ega equ 0 ;1 to assemble for Enhanced Graphics Adapter
- ;0 to assemble for Color Graphics Adapter
- cga equ (ega xor 1) ;the opposite status of ega
- ;
- bios_data_segment segment at 40h ;BIOS keeps its data at 400h;
- org 63h ;at 463h is a word that holds
- bios_crtc_base_address dw ? ; the CRT controller's base
- bios_data_segment ends ; address
- ;
- ;
- cseg segment para public 'cseg'
- assume cs:cseg,ds:cseg,es:nothing
- public initialize,terminate,object_services
- public put_objects_on_screen,vert_int_modulo_count
- ;
- ;Memory for the parameters used to keep track of objects is reserved
- ; below. Many of the parameters stored are very code specific so that
- ; the size and number of objects which could be processed during
- ; vertical non-display time could be maximized.
- ;
- number_of_objects equ 3 ;this should be set to the maximum number
- ; of objects or priorities which will
- ; need to be kept track of at one time.
- ;
- queue label word
- ;
- draw_screen_offset dw ? ;offset in screen memory buffer of upper
- ; left hand corner of object. 0ffffh if
- ; object is to be ignored.
- dist_to_odd_scan_line dw ? ;distance from end of object on an even
- ; scan line to the start of the object
- ; on the next (odd) scan line
- dist_to_even_scan_line dw ? ;distance from end of object on an odd
- ; scan line to the start of the object
- ; on the next (even) scan line
- ;
- erase_parms label word
- ;
- erase_width dw ? ;the object's screen image width in words
- erase_entry_point dw ? ;the address of the inline code to do erase
- erase_screen_offset dw ? ;the address where object was last drawn
- ; 0ffffh if object is not to be erased.
- erase_image_offset dw ? ;used to determine if need to erase when
- ; object is in old position
- ;
- length_of_erase_parms equ $-erase_parms
- ;
- draw_col_entry_point dw ? ;address of the column code for drawing
- draw_row_entry_point dw ? ;address of the row inline code for drawing
- draw_image_offset dw ? ;offset in the code segment of the image
- ;
- queue_item_length equ ($ - queue) ;number of bytes for each item
- distance_from_entry_point_to_next_item equ $ - erase_entry_point
- distance_from_image_to_next_item equ $ - draw_image_offset
- ;
- db ( (number_of_objects-1) * queue_item_length ) dup(?)
- end_of_queue label word
- ;
- vert_int_modulo_count dw 0 ;incremented each time a vertical
- ; interrupt occurs
- background_segment dw ? ;place to hold the paragraph address
- ; of the background buffer used to
- ; erase objects
- crtc_base_address dw ? ;will hold register address
- ;
- old_int10_offset dw ? ;place to store the vector contents
- old_int10_segment dw ? ; so they can be restored when finished
- ;
- old_int_mask db ? ;place to store the mask register's
- ; contents so it can be restored
- ;
- true equ 1 ;used for flag values
- false equ 0 ;
- ;
- need_to_draw_something_flag db false ;true if a change needs to be
- ; made to any of the objects'
- ; screen images
- ;
- screen_buffer_paragraph_adr equ 0b800h
- ;
- ;
- initialize proc near
- cld ;count up
- push ds ;
- mov cs:[background_segment],ax ;store background adr
- mov ax,cs ;make data segment
- mov ds,ax ; same as code segment
- ; since that is where data
- mov es,ax ; used by this routine is
- mov di,offset queue ;turn off all objects
- mov cx,(number_of_objects * queue_item_length)/2
- mov ax,0ffffh ;
- rep stosw ;
- ;
- mov [need_to_draw_something_flag],false ;nothing to draw
- if ega
- sub ax,ax ;swapping interrupt
- mov ds,ax ; vectors with our
- mov bx,(10*4) ; interrupt handler
- mov ax,offset put_objects_on_screen ;our vertical int
- mov dx,cs ; handler address
- cli ;disable interrupts
- xchg [bx],ax ;offset
- xchg [bx+2],dx ;segment
- mov cs:[old_int10_offset],ax ;save old value so we
- mov cs:[old_int10_segment],dx ; can restore it upon
- ; ; termination
- mov ax,bios_data_segment ;find the register
- mov ds,ax ; address
- assume ds:bios_data_segment ;
- mov dx,[bios_crtc_base_address] ;
- mov cs:[crtc_base_address],dx ;save it in code seg
- mov al,11h ;select vertical
- out dx,al ; retrace end register
- mov al,04h ; and flip it off
- inc dx ;
- out dx,al ;
- mov al,14h ; then flip it on
- out dx,al ;
- ;
- in al,21h ;enable IRQ2
- mov cs:[old_int_mask],al ; save old value
- and al,not 4 ;
- out 21h,al ;
- ;
- sti ;enable interrupts
- endif
- pop ds ;restore data segment
- ret
- initialize endp
- ;
- terminate proc near ;only needs to be used when assembled
- if ega ; for use on an EGA
- mov dx,[crtc_base_address]
- mov al,11h
- out dx,al
- inc dx
- mov al,24h ;bit 5 high to disable, bit 4 low to
- out dx,al ; clear vertical interrupt
- push ds
- sub ax,ax ;restore original interrupt
- mov ds,ax ; 10 vector
- mov bx,(10*4) ;
- mov ax,cs:[old_int10_offset] ;
- mov dx,cs:[old_int10_segment] ;
- cli ;make sure interrupt
- mov [bx],ax ; doesn't occur while
- mov [bx+2],dx ; there is an inconsistant
- ; vector/mask
- mov bl,cs:[old_int_mask] ;restore IRQ2 mask bit
- and bl,4 ; to state it had when
- in al,21h ; Initialize was called
- and al,not 4 ;
- or al,bl ;
- out 21h,al ;
- sti
- pop ds
- endif
- ret
- terminate endp
- ;
- object_services proc near
- cld ;
- push es ;save the registers used
- push ds ;
- push ax ;
- push bx ;
- push cx ;
- push si ;
- push di ;
- ;
- mov ax,cs ;everything will be in code segment
- mov es,ax ;
- mov ds,ax ;
-
- ;
- shl di,1 ;multiply object number
- shl di,1 ; which is in DI by 20 to
- mov ax,di ; find object's parameter table
- shl di,1 ; offset in queue structure
- shl di,1 ; (NOTE: If a code change alter
- add di,ax ; queue_item_length this code must
- ; ; be changed!)
- mov ax,offset queue ;point directly to object's first
- add di,ax ; parameter
- mov ax,[bx+even_line_screen_offset_table]
- add ax,cx ;find screen offset of top left corner
- if ega
- cli ;can't allow parameters to be just half
- ; changed if a vertical interrupt occurs
- endif
- cmp si,0ffffh ;if object is to be turned off then
- jne save_position ; need to store a 0ffffh for the draw
- mov ax,si ; screen position
- stosw ;
- jmp short finish_services
- save_position: ;
- stosw ;save as first parameter (draw_screen_offset)
- lodsb ;get the height of the image
- xor ah,ah ;make height a word
- mov bx,ax ;store height
- lodsb ;get the width of the image in bytes
- mov cx,2000h ;calculate amount to add after even scan
- sub cx,ax ; lines are drawn to get the address of the
- xchg ax,cx ; next scan line, and store it in queue
- stosw ;
- mov ax,1fb0h ;calculate amount to subtract after odd scan
- add ax,cx ; lines are drawn to get the address of the
- stosw ; next scan line, and store it in queue
- mov ax,cx ;store the width in queue
- shr ax,1 ; width is stored as number of words
- stosw ;
- mov ax,[bx+erase_inline_vector_table-2]
- ;-2 because there is no 0 lines entry point
- stosw ;store the place to jump to erase an image
- ; of this height
- add di,4 ;skip erase_screen_offset and
- ; erase_image_offset as these are filled
- ; in when an object is drawn
- xchg si,cx ;swap image offset with width
- mov ax,[si+column_inline_vector_table-2] ;inline code adr
- stosw ; driver operates with words, so there is no
- ; need to divide SI by two to do table lookup
- mov ax,[bx+row_inline_vector_table-2] ;inline code adr
- stosw ; which calls column inline code for each row
- mov [di],cx ;last param to put on queue is image offset
- ;
- finish_services:
- mov [need_to_draw_something_flag],true ;record change
- if ega
- sti ;all parameters have been put on queue
- ; so interrupts are safe now
- endif
- pop di ;restore those registers that were used
- pop si ;
- pop cx ;
- pop bx ;
- pop ax ;
- pop ds ;
- pop es ;
- ret ;
- object_services endp
- ;
- ;This table is used to find the offset of an even scan line in the
- ; memory map of the color graphics adapter in medium resolution mode.
- ;
- even_line_screen_offset_table label word
- xx=0
- rept 100 ;there are 100 even lines
- dw xx*50h ; each is 50h (80 decimal) long
- xx=xx+1
- endm
- ;
- use_old_vector:
- pop ax ;restore registers used before
- pop dx ; jumping to previous IRQ2 handler
- jmp dword ptr cs:[old_int10_offset]
- ;
- put_objects_on_screen proc far
- push dx ;save registers used by EGA code
- push ax ;
- if ega
- ;must check if interrupt is being signaled
- ; by the EGA card. If not, it needs to be
- mov dx,3c2h ; handled by another routine in the vector
- in al,dx ; chain. The PC AT in particular uses
- test al,80h ; IRQ2 for multiple devices.
- jz use_old_vector
- endif
- inc cs:[vert_int_modulo_count] ;count vert interrupts
- sti ;enable interrupts
- cmp cs:[need_to_draw_something_flag],true ;anything to do?
- je process_queue ; jmp if there is
- ; otherwise do nothing
- if ega
- cli
- mov al,20h ;issue a non_specific EOI (End Of Interrupt)
- out 20h,al ; so that interrupt controller chip will
- ; acknowledge future vertical interrupts
- mov dx,cs:[crtc_base_address] ;re-enable ega card interrupt
- mov al,11h ; select vertical retrace end
- out dx,al ; register and clear vertical interrupt
- mov al,04h ;
- inc dx ;
- out dx,al ;
- mov al,14h ; then enable vertical interrupt
- out dx,al ;
- endif
- pop ax ;restore original values
- pop dx ;
- iret
- ;
- skip_this_object:
- add si,queue_item_length ;point SI to next item
- cmp si,offset end_of_queue ;see if we are done
- je draw ; jmp to draw if we are
- jmp get_next_objects_screen_adr ; if not, erase next
- ;
- process_queue:
- push bx ;save the rest of the world
- push cx ;
- push bp ;
- push si ;
- push di ;
- push ds ;
- push es ;
- ;
- push cs ;setup environment
- pop ds ; data is in code segment
- cld ; we will count up
- mov ax,screen_buffer_paragraph_adr
- mov es,ax ; ES points to screen memory
- ;
- mov si,offset queue ;point to beginning of queue
- ;
- ;Erase all the active objects which have old screen positions
- ; different from their present screen position or have different
- ; image offsets
- ;
- get_next_objects_screen_adr:
- mov ax,[si+erase_screen_offset-draw_screen_offset]
- ; get the screen buffer
- ; offset of last draw
- cmp ax,0ffffh ;if ffffh then object is
- je skip_this_object ; yet to be drawn
- cmp ax,[si] ;if new and old positions and
- jne erase_this_object ; images are same then skip erase
- mov di,[si+erase_image_offset-draw_screen_offset]
- cmp di,[si+draw_image_offset-draw_screen_offset]
- je skip_this_object ;
- erase_this_object:
- inc si ;point to next parameter
- inc si ;
- mov di,ax ;save screen buffer adr
- lodsw ;get distance to odd scan line
- mov bp,ax ; save in BP for inline code use
- lodsw ;get distance to even scan line
- mov dx,ax ; save in DX for inline code use
- lodsw ;get the width in words for erase
- mov cx,[si] ;get address of erase inline code
- ;
- push si ;save position so next object
- push ds ; can be found
- mov ds,[background_segment] ;stuff to erase with
- call cx ;erase it!
- pop ds ;restore where
- pop si ; we left off in queue
- add si,distance_from_entry_point_to_next_item ;next item
- cmp si,offset end_of_queue ;see if we are done
- jne get_next_objects_screen_adr ; jmp if more to erase
- ;
- ;Draw all the active objects by AND/ORing into screen buffer
- ;
- draw:
- mov si,offset queue ;point to beginning of queue
- ;
- get_next_objects_screen_adr2:
- lodsw ;get screen buffer offset
- mov [si+erase_screen_offset-draw_screen_offset-2],ax
- ;save what will be old position
- mov di,[si+draw_image_offset-draw_screen_offset-2]
- ;save what will be old image offset
- mov [si+erase_image_offset-draw_screen_offset-2],di
- cmp ax,0ffffh ;see if object is active
- je skip_this_object2 ; jmp if it isn't
- mov di,ax ;save screen buffer adr
- lodsw ;get distance to odd scan line
- mov bp,ax ; save in BP for inline code use
- lodsw ;get distance to even scan line
- mov dx,ax ; save in DX for inline code use
- add si,length_of_erase_parms ;skip the erase parameters
- lodsw ;get the draw inline row adr
- mov cx,ax ; save in CX for inline use
- lodsw ;get the draw inline row adr
- mov bx,ax ; save in BX for call to inline
- lodsw ;get draw_image offset
- push si ;save pointer to next queue item
- mov si,ax ;save draw image in SI
- call bx ;draw it!
- pop si ;restore where we left off in queue
- cmp si,offset end_of_queue ;see if we are done
- jne get_next_objects_screen_adr2 ; jmp if more to erase
- ;
- finish_up:
- if ega
- cli
- mov al,20h ;issue a non_specific EOI (End Of Interrupt)
- out 20h,al ; so that interrupt controller chip will
- ; acknowledge future vertical interrupts
- mov dx,cs:[crtc_base_address] ;re-enable interrupt
- mov al,11h ; select vertical retrace end
- out dx,al ; register and clear vertical interrupt
- mov al,04h ;
- inc dx ;
- out dx,al ; then enable vertical interrupt
- mov al,14h ;
- out dx,al ;
- endif
- pop es ;restore all registers
- pop ds ;
- pop di ;
- pop si ;
- pop bp ;
- pop cx ;
- pop bx ;
- pop ax ;
- pop dx ;
- mov cs:[need_to_draw_something_flag],false
- ;indicate no reason to draw again until the
- ; queue is changed
- iret ;restore flags and contine where interrupted
- ;
- skip_this_object2:
- add si,(queue_item_length-2) ;point SI to next item
- cmp si,offset end_of_queue ;see if we are done
- jne get_next_objects_screen_adr2 ; jmp if not
- jmp short finish_up ; jmp if all finished
- put_objects_on_screen endp
- ;
- ;
- ;This is inline code for finding the screen address for each line
- ; of the image and calling the AND-OR inline code.
- ;
- rlabel macro xx ;this macro is used to label the inline code
- rline&xx&: ; entry points
- endm
- ;
- ;
- ; inline code for rows
- ;
- xx=42 ;there will be an entry point for each even
- ; number of lines between 2 and 40. They will
- ; be labeled "rline2", "rline4", ... "rline40"
- rept 20 ;each repeat handles two lines
- xx=xx-2 ;calculate number of lines for entry point
- rlabel %xx ;put in label for entry point
-
- call cx ;CX holds address of inline columns code
- add di,bp ;calculate the address to start next line
- call cx ;process image for odd scan line
- sub di,dx ;calculate the address to start next line
- endm ; the next line will be an even line
- ret
- ;
- ;Inline code for AND-ORing a line of the image into the screen
- ;
- clabel macro xx ;this macro is used to label the inline code
- cline&xx&: ; entry points for number of columns to AND-OR
- endm ;
- ;
- xx=10 ;this code can handle an image up to ten words
- rept 10 ; wide
- clabel %xx ;put in label for entry based on number of
- ; words in a column
- lodsw ;get mask word
- and ax,es:[di] ;mask out background
- or ax,[si] ;insert data word
- inc si ;point to next mask word
- inc si ;
- stosw ;return modified word to memory
- xx=xx-1 ;adjust label number
- endm
- ret ;this return is executed at the end of every
- ; line
- ;
- ;This table is used as an indirect address for jumping into
- ; the inline code for image moving.
- ;
- row_inline_vector_table label word ;there is no entry point for zero
- ; lines. Starting at 2 eliminates
- ; the need to store a dummy entry
- ; point address
- row_entry_address macro xx ;this macro is used to generate
- dw rline&xx& ; the labels corresponding to the
- endm ; inline code entry points
- ;
- xx=2
- rept 20
- row_entry_address %xx
- xx=xx+2
- endm
- ;
- ;This table is used as an indirect address for jumping into
- ; the inline code for exclusive-ORing columns.
- ;
- column_inline_vector_table label word ;there is no entry point for zero
- ; lines. Starting at 2 eliminates
- ; the need to store a dummy entry
- ; point address
- column_entry_address macro xx ;this macro is used to generate
- dw cline&xx& ; the labels corresponding to the
- endm ; inline code entry points
- ;
- xx=1
- rept 10
- column_entry_address %xx
- xx=xx+1
- endm
- ;
- ;This is inline code for erasing the image by restoring the screen
- ; memory map from the background buffer.
- ;
- elabel macro xx ;this macro is used to label the inline code
- eline&xx&: ; entry points
- endm
- ;
- xx=42 ;there will be an entry point for each even
- ; number of lines between 2 and 40. They will
- ; be labeled "eline2", "eline4", ... "eline40"
- rept 20
- xx=xx-2 ;calculate number of lines for this entry point
- elabel %xx ;put in label for entry point
- mov si,di ;erase using same offset in background buffer
- mov cx,ax ;put width of image in words in CX to prepare for
- rep movsw ; repeated move string on even line
- add di,bp ;calculate address of next line DI + (2000h-width)
- mov si,di ;erase using same offset in background buffer
- mov cx,ax ;put width of image in bytes in CX to prepare for
- rep movsw ; repeated move string on odd line
- sub di,dx ;calculate address of next line DI - (1fb0h+width)
- endm
- ret
- ;
- ;This table is used as an indirect address for jumping into
- ; the inline code for erasing an image.
- ;
- erase_inline_vector_table label word ;there is no entry point for zero
- ; lines. Starting at 2 eliminates
- ; the need to store a dummy entry
- ; point address
- entry_address macro xx ;this macro is used to generate
- dw eline&xx& ; the labels corresponding to the
- endm ; inline code entry points
- ;
- xx=2
- rept 20
- entry_address %xx
- xx=xx+2
- endm
- ;
- cseg ends
- end